home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Deutsche Edition 1
/
Deutsche Edition 1.iso
/
amok
/
amok_lha
/
amok71.lha
/
XStat
/
XStat.mod
< prev
next >
Wrap
Text File
|
1993-08-15
|
34KB
|
1,159 lines
(***************************************************************************
:Program. XStat
:Author. Jürgen Weinelt
:Address. Zur Kanzel 1, D-8783 Hammelburg, Germany
:Version. 1.03 (02-Apr-92)
:Copyright. Freeware
:Language. Modula-2
:Translator. M2Amiga V4.097d
:History. 1.00 initial release
:History. 1.01 bug fix: used to guru when xfer = 0 bytes
:History. 1.02 added peak cps rating
:History. 1.03 added monthly statistics
:Contents. Extract statistics from UUCiCo "Xferstat" logfile
:Contents. Requires UUCiCo 1.15c (or later) by Andrew "Charly" Kopp
**************************************************************************)
MODULE XStat;
(*$ StackChk:=FALSE *)
(*$ CaseChk:=FALSE *)
(*$ Volatile:=FALSE *)
IMPORT Arts;
IMPORT ASCII;
IMPORT Break;
IMPORT DosD;
IMPORT DosL;
IMPORT ExecD;
IMPORT ExecL;
IMPORT InOut;
IMPORT R;
IMPORT RealConversions;
IMPORT RealInOut;
IMPORT String;
IMPORT SYSTEM;
CONST
strlen=100;
dmult=24*60*60;
hmult=60*60;
mmult=60;
smult=1;
dsun=0*dmult;
dmon=1*dmult;
dtue=2*dmult;
dwed=3*dmult;
dthu=4*dmult;
dfri=5*dmult;
dsat=6*dmult;
defxstatdata="UULIB:XStat.data";
defxferstat="UUSPOOL:XferStat";
vers="XStat 1.03";
version="$VER: "+vers+" (02-Apr-92)";
TYPE
StringT=ARRAY[0..strlen] OF CHAR;
StringTPtr=POINTER TO StringT;
File=
RECORD
fh: DosD.FileHandlePtr;
eof: BOOLEAN;
END;
CostPtr=POINTER TO Cost;
Cost=
RECORD
next: CostPtr;
start: LONGINT; (* start time for mpu and unit *)
stop: LONGINT; (* end time for mpu and unit *)
mpu: REAL; (* price per unit *)
unit: REAL; (* unit duration *)
END;
XSDataPtr=POINTER TO XSData;
XSData=
RECORD
next: XSDataPtr;
host: StringT; (* host name *)
costlist: CostPtr; (* list of Cost records *)
END;
Connect=
RECORD
host: StringT; (* host name *)
callout: BOOLEAN; (* TRUE: outgoing call *)
start: LONGINT; (* session start time *)
stop: LONGINT; (* session end time *)
sdate: DosD.Date; (* session start time in dos format *)
inbb: LONGINT; (* brutto bytes in *)
outbb: LONGINT; (* brutto bytes out *)
inbn: LONGINT; (* netto bytes in *)
outbn: LONGINT; (* netto bytes out *)
cost: REAL; (* connection phone costs *)
units: LONGINT; (* phone units consumed *)
seconds: LONGINT; (* online time in seconds *)
END;
Statistics=
RECORD
connects: LONGINT;
inbb: LONGINT;
outbb: LONGINT;
inbn: LONGINT;
outbn: LONGINT;
cost: REAL;
units: LONGINT;
seconds: LONGINT;
bpeak: LONGINT;
npeak: LONGINT;
END;
Args=
RECORD
xstatdata: StringT; (* xstat.data name and path; -d *)
xferstat: StringT; (* xferstat name and path; -s *)
verbose: BOOLEAN; (* lotsa output; -v; off *)
from: DosD.Date; (* from date; -f; zero *)
to: DosD.Date; (* to date; -t; TODAY *)
host: StringT; (* for host name only; -n; off *)
in: BOOLEAN; (* process incoming calls; -i; on *)
out: BOOLEAN; (* process outgoing calls; -o; on *)
quiet: BOOLEAN; (* suppress non-fatal errors; -q; off *)
monthly: BOOLEAN; (* monthly mode; -m; off *)
END;
VAR
inf: File;
ln1,ln2,temp: StringT;
log: Connect;
totali,totalo: Statistics;
xsroot: XSDataPtr;
args: Args;
currency: StringT;
(*---------------------------------------------------------------------------
FreeCostChain: FreeMem() a linked list of "Cost" records
---------------------------------------------------------------------------*)
PROCEDURE FreeCostChain(d:CostPtr);
VAR
n: CostPtr;
BEGIN
REPEAT
n:=d^.next;
ExecL.FreeMem(d,SIZE(d^));
d:=n;
UNTIL n=NIL;
END FreeCostChain;
(*---------------------------------------------------------------------------
FreeXData: FreeMem() a linked list of "XSData" records, freeing the
attached "Cost" record lists as well.
---------------------------------------------------------------------------*)
PROCEDURE FreeXData(d: XSDataPtr);
VAR
n: XSDataPtr;
BEGIN
REPEAT
n:=d^.next;
FreeCostChain(d^.costlist);
ExecL.FreeMem(d,SIZE(d^));
d:=n;
UNTIL n=NIL;
END FreeXData;
(*---------------------------------------------------------------------------
MyError: Display error messages, terminate program if necessary
---------------------------------------------------------------------------*)
(*$ CopyDyn:=FALSE *)
PROCEDURE MyError(x1,x2,x3: ARRAY OF CHAR; fatal: BOOLEAN);
BEGIN
IF fatal THEN
InOut.WriteString("FATAL ERROR ");
InOut.WriteString(x1); InOut.WriteLn;
InOut.WriteString(x2); InOut.WriteLn;
InOut.WriteString(x3); InOut.WriteLn;
InOut.WriteLn;
Arts.Exit(20);
ELSIF (NOT args.quiet) OR ((x1[0]="F") & (x1[1]="A") & (x1[2]="T")) THEN
InOut.WriteString(x1); InOut.WriteLn;
InOut.WriteString(x2); InOut.WriteLn;
InOut.WriteString(x3); InOut.WriteLn;
InOut.WriteLn;
END;
END MyError;
(*---------------------------------------------------------------------------
GetHead: Remove first char from a string, return it in char variable
---------------------------------------------------------------------------*)
PROCEDURE GetHead(VAR s: StringT; VAR c: CHAR);
BEGIN
c:=s[0];
String.DeleteChar(s,0);
END GetHead;
(*---------------------------------------------------------------------------
KillSpaces: Remove leading spaces from a string
---------------------------------------------------------------------------*)
PROCEDURE KillSpaces(VAR l1: StringT);
VAR
c: CHAR;
BEGIN
WHILE l1[0]=" " DO
GetHead(l1,c);
END;
END KillSpaces;
(*---------------------------------------------------------------------------
Get: Remove first "word" from a string. "Word" means "a sequence of
non-spaces, terminated by at least one space". Return this word
as a separate string
---------------------------------------------------------------------------*)
PROCEDURE Get(VAR s1,s2: StringT): BOOLEAN;
VAR
c: CHAR;
BEGIN
s2[0]:=ASCII.nul;
KillSpaces(s1);
GetHead(s1,c);
WHILE (c#" ") AND (c#ASCII.nul) DO
Break.TestBreak();
String.ConcatChar(s2,c);
GetHead(s1,c);
END;
RETURN String.Length(s2)>0;
END Get;
(*---------------------------------------------------------------------------
ConvDate: Convert ASCII representations of date and time to Dos format
---------------------------------------------------------------------------*)
(*$ CopyDyn:=FALSE *)
PROCEDURE ConvDate(ds,ts: StringT; VAR dat: DosD.Date): BOOLEAN;
VAR
dt: DosD.DateTime;
res: LONGINT;
BEGIN
dt.format:=DosD.formatDOS;
dt.flags:=DosD.DateTimeFlagSet{};
dt.strDate:=SYSTEM.ADR(ds);
dt.strTime:=SYSTEM.ADR(ts);
res:=DosL.StrToDate(SYSTEM.ADR(dt));
dat:=dt.date;
RETURN res=0;
END ConvDate;
(*---------------------------------------------------------------------------
ParseArg: Parse argument string (single argument) and set "args" record
accordingly
---------------------------------------------------------------------------*)
PROCEDURE ParseArg(VAR s: StringT): BOOLEAN;
VAR
c: CHAR;
s1,ts: StringT;
BEGIN
s1:=s;
GetHead(s,c);
IF c#"-" THEN
MyError("FATAL ERROR","illegal argument",s1,FALSE);
RETURN TRUE;
ELSE
GetHead(s,c);
CASE c OF
|"d","D": args.xstatdata:=s;
|"s","S": args.xferstat:=s;
|"v","V": args.verbose:=TRUE;
|"f","F": IF NOT args.monthly THEN
IF ConvDate(s,"00:00:00",args.from) THEN
MyError("FATAL ERROR","illegal FROM date",s1,FALSE);
RETURN TRUE;
END;
END;
|"t","T": IF NOT args.monthly THEN
IF ConvDate(s,"23:59:59",args.to) THEN
MyError("FATAL ERROR","illegal TO date",s1,FALSE);
RETURN TRUE;
END;
END;
|"n","N": args.host:=s;
|"i","I": args.in:=FALSE;
|"o","O": args.out:=FALSE;
|"q","Q": args.quiet:=TRUE;
|"m","M": ts:="01-";
String.Concat(ts,s);
IF ConvDate(ts,"00:00:00",args.from) THEN
MyError("FATAL ERROR","illegal MONTH date",s1,FALSE);
RETURN TRUE;
END;
ts:="31-";
String.Concat(ts,s);
IF ConvDate(ts,"23:59:59",args.to) THEN
ts:="30-";
String.Concat(ts,s);
IF ConvDate(ts,"23:59:59",args.to) THEN
ts:="29-";
String.Concat(ts,s);
IF ConvDate(ts,"23:59:59",args.to) THEN
ts:="28-";
String.Concat(ts,s);
IF ConvDate(ts,"23:59:59",args.to) THEN
MyError("FATAL ERROR","illegal MONTH date",s1,FALSE);
RETURN TRUE;
END;
END;
END;
END;
args.monthly:=TRUE;
|ELSE MyError("FATAL ERROR","unknown argument",s1,FALSE);
RETURN TRUE;
END;
END;
RETURN FALSE;
END ParseArg;
(*---------------------------------------------------------------------------
SplitArgs: Split multi-argument string into single arguments and feed
them through ParseArg()
---------------------------------------------------------------------------*)
PROCEDURE SplitArgs(VAR s: StringT);
VAR
str: StringT;
c: CHAR;
argerr,quotes: BOOLEAN;
BEGIN
argerr:=FALSE;
REPEAT
quotes:=FALSE;
str[0]:=ASCII.nul;
KillSpaces(s);
WHILE ((s[0]#" ") OR quotes) AND (s[0]#ASCII.nul) DO
GetHead(s,c);
IF c='"' THEN
quotes:=NOT quotes;
ELSE
String.ConcatChar(str,c);
END;
END;
IF quotes THEN
MyError("FATAL ERROR in command line",
"non-terminated quotes",str,FALSE);
argerr:=TRUE;
ELSE
IF str[0]#ASCII.nul THEN
argerr:=argerr OR ParseArg(str);
END;
END;
UNTIL s[0]=ASCII.nul;
IF argerr THEN
Arts.Exit(20);
END;
END SplitArgs;
(*---------------------------------------------------------------------------
PresetArgs: Set default values for "args" record
---------------------------------------------------------------------------*)
PROCEDURE PresetArgs();
VAR
bdummy: BOOLEAN;
BEGIN
args.xstatdata:=defxstatdata;
args.xferstat:=defxferstat;
args.verbose:=FALSE;
bdummy:=ConvDate("01-JAN-78","00:00:00",args.from);
bdummy:=ConvDate("TODAY","23:59:59",args.to);
args.host[0]:=ASCII.nul;
args.in:=TRUE;
args.out:=TRUE;
args.quiet:=FALSE;
args.monthly:=FALSE;
END PresetArgs;
(*---------------------------------------------------------------------------
GetLine: Read a line from the input file
---------------------------------------------------------------------------*)
PROCEDURE GetLine(VAR line: StringT);
VAR
res: LONGINT;
BEGIN
IF inf.fh#NIL THEN
res:=DosL.FGets(inf.fh,SYSTEM.ADR(line),strlen);
inf.eof:=(res=0) AND (DosL.IoErr()=0);
END;
IF (String.Length(line)>0) AND (line[String.Length(line)-1]=ASCII.lf) THEN
line[String.Length(line)-1]:=ASCII.nul;
END;
END GetLine;
(*---------------------------------------------------------------------------
GetLog: Read a two-line log entry from Xferstat
---------------------------------------------------------------------------*)
PROCEDURE GetLog(VAR l1,l2: StringT);
BEGIN
l1[0]:=ASCII.nul;
l2[0]:=ASCII.nul;
REPEAT
Break.TestBreak();
GetLine(l1);
UNTIL (inf.eof) OR (l1[0]="<") OR (l1[0]=">");
IF NOT inf.eof THEN
GetLine(l2);
END;
END GetLog;
(*---------------------------------------------------------------------------
Skip: Skip the first "word" of a string; refer to "Get()" for an
explanation ot the term "word"
---------------------------------------------------------------------------*)
PROCEDURE Skip(VAR s: StringT);
VAR
dum: BOOLEAN;
x: StringT;
BEGIN
dum:=Get(s,x);
END Skip;
(*---------------------------------------------------------------------------
GetLNum: Get the first "word" of a string, and convert it to a LONGINT
(if possible). Refer to "Get()" for an explanation of the
term "word"
---------------------------------------------------------------------------*)
PROCEDURE GetLNum(VAR s: StringT; VAR x: LONGINT): BOOLEAN;
VAR
sn: StringT;
res: LONGINT;
BEGIN
IF Get(s,sn) THEN
res:=DosL.StrToLong(SYSTEM.ADR(sn),x);
RETURN res>0;
END;
RETURN FALSE;
END GetLNum;
(*---------------------------------------------------------------------------
GetNum: Call "GetLNum()" and make sure the result is an INTEGER
---------------------------------------------------------------------------*)
PROCEDURE GetNum(VAR s: StringT; VAR x: INTEGER): BOOLEAN;
VAR
l: LONGINT;
BEGIN
IF GetLNum(s,l) THEN
IF l>MAX(INTEGER) THEN
x:=0;
ELSE
x:=INTEGER(l);
RETURN TRUE;
END;
ELSE
x:=0;
END;
RETURN FALSE;
END GetNum;
(*---------------------------------------------------------------------------
GetReal: Get the first "word" of a string, and convert it to a REAL
(if possible). Refer to "Get()" for an explanation of the
term "word"
---------------------------------------------------------------------------*)
PROCEDURE GetReal(VAR s: StringT; VAR x: REAL): BOOLEAN;
VAR
sn: StringT;
err: BOOLEAN;
BEGIN
IF Get(s,sn) THEN
RealConversions.StrToReal(sn,x,err);
RETURN NOT err;
END;
RETURN FALSE;
END GetReal;
(*---------------------------------------------------------------------------
GetTime: Get the first two "words" of a string, and convert them to
Dos format date and time (if possible). Refer to "Get()" for an
explanation of the term "word"
---------------------------------------------------------------------------*)
PROCEDURE GetTime(VAR s: StringT; VAR t: LONGINT; VAR dos: DosD.Date): BOOLEAN;
VAR
ds,ts: StringT;
dt: DosD.DateTime;
BEGIN
IF Get(s,ds) THEN
IF Get(s,ts) THEN
dt.format:=DosD.formatCDN;
dt.flags:=DosD.DateTimeFlagSet{};
dt.strDate:=SYSTEM.ADR(ds);
dt.strTime:=SYSTEM.ADR(ts);
IF DosL.StrToDate(SYSTEM.ADR(dt))#0 THEN
dos:=dt.date;
t:=(dos.days MOD 7)*dmult+(dos.minute*mmult)+(dos.tick DIV 50);
RETURN TRUE;
END;
END;
END;
RETURN FALSE;
END GetTime;
(*---------------------------------------------------------------------------
TestXStatHeader: Check if the input file's header is "H XSTAT DATA"
---------------------------------------------------------------------------*)
PROCEDURE TestXStatHeader();
VAR
s1,s,t: StringT;
c: CHAR;
BEGIN
GetLine(s);
s1:=s;
GetHead(s,c);
IF c="H" THEN
IF Get(s,t) OR (String.Compare(t,"XSTAT")#0) THEN
IF NOT (Get(s,t) OR (String.Compare(t,"DATA")#0)) THEN
MyError("in XStat.data header",
s1,"keyword DATA not found",TRUE);
END;
ELSE
MyError("in XStat.data header",
s1,"keyword XSTAT not found",TRUE);
END;
ELSE
MyError("in XStat.data header",
s1,"header identifier H not found",TRUE);
END;
END TestXStatHeader;
(*---------------------------------------------------------------------------
ParseXSDataNEntry: Parse a "host name" line from XStat.data
---------------------------------------------------------------------------*)
PROCEDURE ParseXSDataNEntry(VAR s,s1: StringT; VAR cxsd: XSDataPtr);
VAR
xsd: XSData;
t: StringT;
BEGIN
IF (cxsd#NIL) AND (cxsd^.costlist=NIL) THEN
MyError("in XStat.data",
"N-line without subsequent C-lines for",
cxsd^.host,TRUE);
END;
IF Get(s,t) THEN
xsd.next:=NIL;
xsd.host:=t;
xsd.costlist:=NIL;
cxsd:=xsroot;
IF xsroot#NIL THEN
WHILE cxsd^.next#NIL DO
cxsd:=cxsd^.next;
END;
cxsd^.next:=ExecL.AllocMem(SIZE(xsd),ExecD.MemReqSet{});
IF cxsd^.next=NIL THEN
MyError("","not enough memory","",TRUE);
END;
cxsd:=cxsd^.next;
ELSE
xsroot:=ExecL.AllocMem(SIZE(xsd),ExecD.MemReqSet{});
IF xsroot=NIL THEN
MyError("","not enough memory","",TRUE);
END;
cxsd:=xsroot;
END;
cxsd^:=xsd;
ELSE
MyError("in XStat.data",
s1,"no host name found in N-line",TRUE);
END;
END ParseXSDataNEntry;
(*---------------------------------------------------------------------------
ParseXSDataCEntry: Parse a "cost info" line from XStat.data
---------------------------------------------------------------------------*)
PROCEDURE ParseXSDataCEntry(VAR s,s1: StringT; VAR cxsd: XSDataPtr);
VAR
t,t1: StringT;
cost: Cost;
ccp: CostPtr;
temp: INTEGER;
c0{R.D7},c1{R.D6}: CHAR;
BEGIN
IF cxsd#NIL THEN
cost.next:=NIL;
cost.stop:=7*dmult-1;
IF Get(s,t) AND
(t[2]="-") AND (t[5]=":") AND (t[8]=":") AND
(String.Length(t)=11) THEN
c0:=t[0]; c1:=t[1];
IF (c0="S") AND (c1="U") THEN
cost.start:=dsun;
ELSIF (c0="M") AND (c1="O") THEN
cost.start:=dmon;
ELSIF (c0="T") AND (c1="U") THEN
cost.start:=dtue;
ELSIF (c0="W") AND (c1="E") THEN
cost.start:=dwed;
ELSIF (c0="T") AND (c1="H") THEN
cost.start:=dthu;
ELSIF (c0="F") AND (c1="R") THEN
cost.start:=dfri;
ELSIF (c0="S") AND (c1="A") THEN
cost.start:=dsat;
ELSE
MyError("in XStat.data",
s1,"illegal day of week in start time in C-line",TRUE);
END;
t[0]:=" "; t[1]:=" "; t[2]:=" "; t[5]:=" "; t[8]:=" ";
IF GetNum(t,temp) THEN
cost.start:=cost.start+hmult*LONGINT(temp);
IF GetNum(t,temp) THEN
cost.start:=cost.start+mmult*LONGINT(temp);
IF GetNum(t,temp) THEN
cost.start:=cost.start+smult*LONGINT(temp);
IF GetReal(s,cost.unit) THEN
IF GetReal(s,cost.mpu) THEN
ccp:=cxsd^.costlist;
IF ccp#NIL THEN
WHILE ccp^.next#NIL DO
Break.TestBreak();
ccp:=ccp^.next;
END;
ccp^.next:=ExecL.AllocMem(SIZE(Cost),ExecD.MemReqSet{});
IF ccp^.next=NIL THEN
MyError("","not enough memory","",TRUE);
END;
ccp^.next^:=cost;
ccp^.stop:=cost.start-1;
IF ccp^.stop<ccp^.start THEN
MyError("in XStat.data","connect start time sequence error",
"(you probably swapped some C-lines, or you left out an N-line)",
TRUE);
END;
ELSE
cxsd^.costlist:=ExecL.AllocMem(SIZE(Cost),ExecD.MemReqSet{});
IF cxsd^.costlist=NIL THEN
MyError("","not enough memory","",TRUE);
END;
cxsd^.costlist^:=cost;
IF cost.start#0 THEN
MyError("in XStat.data",
s1,"start time in first C-line must be SU-00:00:00",TRUE);
END;
END;
ELSE
MyError("in XStat.data",
s1,"illegal 'money per unit' in C-line",TRUE);
END;
ELSE
MyError("in XStat.data",
s1,"illegal 'seconds per unit' in C-line",TRUE);
END;
ELSE
MyError("in XStat.data",
s1,"illegal 'seconds' part in start time in C-line",TRUE);
END;
ELSE
MyError("in XStat.data",
s1,"illegal 'minutes' part in start time in C-line",TRUE);
END;
ELSE
MyError("in XStat.data",
s1,"illegal 'hours' part in start time in C-line",TRUE);
END;
ELSE
MyError("in XStat.data",
s1,"illegal or missing start time in C-line",TRUE);
END;
ELSE
MyError("in XStat.data",
s1,"C-line without corresponding N-line",TRUE);
END;
END ParseXSDataCEntry;
(*---------------------------------------------------------------------------
ParseXSDataSEntry: Parse a "currency sign info" line from XStat.data
---------------------------------------------------------------------------*)
PROCEDURE ParseXSDataSEntry(VAR s,s1: StringT; VAR cxsd: XSDataPtr);
VAR
t: StringT;
BEGIN
IF (cxsd#NIL) THEN
MyError("in XStat.data",
s1,"S-line after the first connection cost record",TRUE);
END;
IF Get(s,t) THEN
IF currency[0]=ASCII.nul THEN
currency:=t;
IF currency[0]#" " THEN
String.Insert(currency,0," \o");
END;
ELSE
MyError("in XStat.data",
s1,"duplicate S-line found",TRUE);
END;
ELSE
MyError("in XStat.data",
s1,"missing currency sign in S-line",TRUE);
END;
END ParseXSDataSEntry;
(*---------------------------------------------------------------------------
GetXStatData: Read and parse the XStat.data file
---------------------------------------------------------------------------*)
PROCEDURE GetXStatData();
VAR
s1,s: StringT;
c: CHAR;
cxsd: XSDataPtr;
BEGIN
cxsd:=NIL;
TestXStatHeader();
REPEAT
Break.TestBreak();
GetLine(s);
s1:=s;
GetHead(s,c);
CASE c OF
|"#": (* NOP *)
|"N": ParseXSDataNEntry(s,s1,cxsd);
|"C": ParseXSDataCEntry(s,s1,cxsd);
|"S": ParseXSDataSEntry(s,s1,cxsd);
|ELSE (* unknown: NOP *)
END;
UNTIL inf.eof;
END GetXStatData;
(*---------------------------------------------------------------------------
ParseLog: Parse a two-line connection data entry from Xferstat
---------------------------------------------------------------------------*)
(*$ CopyDyn:=FALSE *)
PROCEDURE ParseLog(line1,line2: StringT; VAR log: Connect): BOOLEAN;
VAR
c: CHAR;
l1,l2: StringT;
dummy: DosD.Date;
BEGIN
l1:=line1;
l2:=line2;
GetHead(l1,c);
log.callout:=(c="<");
IF Get(l1,log.host) THEN
IF (log.host[0]>="0") AND (log.host[0]<="9") THEN
MyError("invalid log entry: can't find host name",line1,line2,FALSE);
RETURN FALSE;
END;
IF GetTime(l1,log.start,log.sdate) THEN
Skip(l1);
IF GetTime(l1,log.stop,dummy) THEN
GetHead(l2,c);
IF c="|" THEN
Skip(l2);
Skip(l2);
Skip(l2);
IF GetLNum(l2,log.inbb) THEN
IF GetLNum(l2,log.outbb) THEN
Skip(l2);
IF GetLNum(l2,log.inbn) THEN
IF GetLNum(l2,log.outbn) THEN
RETURN TRUE;
ELSE
MyError("invalid log entry: can't get netto send bytes",line1,line2,FALSE);
END;
ELSE
MyError("invalid log entry: can't get netto receive bytes",line1,line2,FALSE);
END;
ELSE
MyError("invalid log entry: can't get brutto send bytes",line1,line2,FALSE);
END;
ELSE
MyError("invalid log entry: can't get brutto receive bytes",line1,line2,FALSE);
END;
ELSE
MyError("invalid log entry: illegal continuation line header ",line1,line2,FALSE);
END;
ELSE
MyError("invalid log entry: can't compute end time",line1,line2,FALSE);
END;
ELSE
MyError("invalid log entry: can't compute start time",line1,line2,FALSE);
END;
ELSE
MyError("invalid log entry: can't find host name",line1,line2,FALSE);
END;
RETURN FALSE;
END ParseLog;
(*---------------------------------------------------------------------------
ComputeCost: Compute the cost for a single connect
---------------------------------------------------------------------------*)
PROCEDURE ComputeCost(VAR log: Connect): BOOLEAN;
VAR
cxsd: XSDataPtr;
ccost: CostPtr;
time: REAL;
BEGIN
log.seconds:=log.stop-log.start+1;
IF log.seconds<0 THEN
log.seconds:=log.seconds+7*dmult;
END;
cxsd:=xsroot;
WHILE (cxsd#NIL) AND (String.Compare(cxsd^.host,log.host)#0) DO
Break.TestBreak();
cxsd:=cxsd^.next;
END;
IF cxsd=NIL THEN
MyError("ERROR: no connection cost data found for host",log.host,
"extend your XStat.data file!",FALSE);
RETURN FALSE;
END;
ccost:=cxsd^.costlist;
WHILE log.start>ccost^.stop DO
Break.TestBreak();
ccost:=ccost^.next;
END;
IF log.stop<log.start THEN
log.stop:=log.stop+7*dmult;
END;
log.cost:=0.0;
log.units:=0;
time:=REAL(log.start);
REPEAT
Break.TestBreak();
log.cost:=log.cost+ccost^.mpu;
time:=time+ccost^.unit;
INC(log.units);
IF LONGINT(time)>ccost^.stop THEN
ccost:=ccost^.next;
IF ccost=NIL THEN
ccost:=cxsd^.costlist;
time:=time-REAL(7*dmult);
log.stop:=log.stop-7*dmult;
END;
END;
UNTIL LONGINT(time)>log.stop;
RETURN TRUE;
END ComputeCost;
(*---------------------------------------------------------------------------
DisplayStats: Display the statistics for a single connect
---------------------------------------------------------------------------*)
PROCEDURE DisplayStats(log: Connect);
VAR
name: StringT;
BEGIN
name:=log.host;
WHILE String.Length(name)<8 DO
String.Insert(name,0," \o");
END;
InOut.WriteString("\nhost name\t"); InOut.WriteString(name);
InOut.WriteString("\ndirection\t "); IF log.callout THEN
InOut.WriteString("out");
ELSE
InOut.WriteString(" in");
END;
InOut.WriteString("\nonline time\t"); InOut.WriteInt(log.seconds,8); InOut.WriteString(" seconds");
InOut.WriteString("\nunits\t\t"); InOut.WriteInt(log.units,8); InOut.WriteString(" units");
InOut.WriteString("\ncost\t\t"); RealInOut.WriteReal(log.cost,8,2); InOut.Write(" "); InOut.WriteString(currency);
InOut.WriteString("\n\n\n\n");
END DisplayStats;
(*---------------------------------------------------------------------------
DisplayTotals: Display the overall statistics
---------------------------------------------------------------------------*)
PROCEDURE DisplayTotals(tot: Statistics; callout: BOOLEAN);
BEGIN
InOut.WriteString("Connection statistics for ");
IF callout THEN
InOut.WriteString("outgoing");
ELSE
InOut.WriteString("incoming");
END;
InOut.WriteString(" calls:\n");
InOut.WriteString("-----------------------------------------\n");
IF tot.connects=0 THEN
InOut.WriteString("\nno connects recorded.\n\n\n\n");
ELSE
InOut.WriteString("\nconnects "); InOut.WriteInt(tot.connects,8);
InOut.WriteString("\nonline time "); InOut.WriteInt(tot.seconds,8); InOut.WriteString(" sec\t ("); InOut.WriteInt((tot.seconds+tot.connects-1) DIV tot.connects,8); InOut.WriteString(" sec/connect)");
InOut.WriteString("\nunits "); InOut.WriteInt(tot.units,8); InOut.WriteString(" units\t ("); RealInOut.WriteReal(REAL(tot.units)/REAL(tot.connects),8,3); InOut.WriteString(" units/connect)");
InOut.WriteString("\ncost "); RealInOut.WriteReal(tot.cost,8,2); InOut.WriteString(currency); InOut.WriteString("\t ("); RealInOut.WriteReal(tot.cost/REAL(tot.connects),8,3); InOut.WriteString(currency); InOut.WriteString("/connect)");
InOut.WriteString("\n\nbrutto read "); InOut.WriteInt(tot.inbb,8); InOut.WriteString(" bytes\t ("); InOut.WriteInt((tot.inbb+tot.connects-1) DIV tot.connects,8); InOut.WriteString(" bytes/connect)");
InOut.WriteString("\nbrutto send "); InOut.WriteInt(tot.outbb,8); InOut.WriteString(" bytes\t ("); InOut.WriteInt((tot.outbb+tot.connects-1) DIV tot.connects,8); InOut.WriteString(" bytes/connect)");
InOut.WriteString("\nnetto read "); InOut.WriteInt(tot.inbn,8); InOut.WriteString(" bytes\t ("); InOut.WriteInt((tot.inbn+tot.connects-1) DIV tot.connects,8); InOut.WriteString(" bytes/connect)");
InOut.WriteString("\nnetto send "); InOut.WriteInt(tot.outbn,8); InOut.WriteString(" bytes\t ("); InOut.WriteInt((tot.outbn+tot.connects-1) DIV tot.connects,8); InOut.WriteString(" bytes/connect)\n");
InOut.WriteString("\nø brutto speed "); InOut.WriteInt((tot.inbb+tot.outbb) DIV tot.seconds,8); InOut.WriteString(" cps\t ("); InOut.WriteInt(tot.bpeak,8); InOut.WriteString(" cps peak)");
InOut.WriteString("\nø netto speed "); InOut.WriteInt((tot.inbn+tot.outbn) DIV tot.seconds,8); InOut.WriteString(" cps\t ("); InOut.WriteInt(tot.npeak,8); InOut.WriteString(" cps peak)");
InOut.WriteString("\nø brutto cost ");
IF tot.inbb+tot.outbb=0 THEN
InOut.WriteString(" -");
ELSE
RealInOut.WriteReal(tot.cost/REAL(tot.inbb+tot.outbb)*1048576.0,8,3);
END;
InOut.WriteString(currency); InOut.WriteString("/MB");
InOut.WriteString("\nø netto cost ");
IF tot.inbn+tot.outbn=0 THEN
InOut.WriteString(" -");
ELSE
RealInOut.WriteReal(tot.cost/REAL(tot.inbn+tot.outbn)*1048576.0,8,3);
END;
InOut.WriteString(currency); InOut.WriteString("/MB\n\n\n\n");
END;
END DisplayTotals;
(*---------------------------------------------------------------------------
UpdateTotals: Update the totali/totalo records
---------------------------------------------------------------------------*)
PROCEDURE UpdateTotals(VAR tot: Statistics; VAR log: Connect);
VAR
tempspeed: LONGINT;
BEGIN
INC(tot.connects);
tot.seconds:=tot.seconds+log.seconds;
tot.inbb:=tot.inbb+log.inbb;
tot.outbb:=tot.outbb+log.outbb;
tot.inbn:=tot.inbn+log.inbn;
tot.outbn:=tot.outbn+log.outbn;
tot.units:=tot.units+log.units;
tot.cost:=tot.cost+log.cost;
IF log.seconds#0 THEN
tempspeed:=(log.inbb+log.outbb) DIV log.seconds;
IF tempspeed>tot.bpeak THEN tot.bpeak:=tempspeed END;
tempspeed:=(log.inbn+log.outbn) DIV log.seconds;
IF tempspeed>tot.npeak THEN tot.npeak:=tempspeed END;
END;
END UpdateTotals;
BEGIN
SYSTEM.SETREG(11,SYSTEM.ADR(version));
xsroot:=NIL;
inf.fh:=NIL;
IF (Arts.kickVersion<36) OR (DosL.dosVersion<36) THEN
MyError("","You need OS 2.0 or later (dos.library V36 or later)",
"",TRUE);
END;
Break.InstallException;
PresetArgs();
InOut.WriteString("\n\n"+vers+"\n");
InOut.WriteString("© Copyright 1992 by Jürgen Weinelt\n");
InOut.WriteString("XStat is Freeware - read the docs for details.\n\n\n");
IF NOT Arts.wbStarted THEN
IF Arts.dosCmdLen>0 THEN
temp:=SYSTEM.CAST(StringTPtr,Arts.dosCmdBuf)^;
IF (temp[0]="?") OR ((temp[0]="-") AND
(temp[1]="?") OR (temp[1]="h") OR (temp[1]="H")) THEN
temp:=SYSTEM.CAST(StringTPtr,Arts.programName)^;
InOut.WriteString(temp);
InOut.WriteString(" [?|-?|-h] [-dname] [-sname] [-v] [-fdate] [-tdate] [-i] [-o] [-nname]\n\n");
InOut.WriteString(" -dname XStat.data file def="); InOut.WriteString(defxstatdata);
InOut.WriteString("\n -sname Xferstat file def="); InOut.WriteString(defxferstat);
InOut.WriteString("\n -v verbose mode def=off\n");
InOut.WriteString(" -fdate ignore calls before date def=01-JAN-78\n");
InOut.WriteString(" -tdate ignore calls after date def=TODAY\n");
InOut.WriteString(" -mmonth monthly, override -f/-t def=off (format MMM-YY)\n");
InOut.WriteString(" -i incoming calls def=on\n");
InOut.WriteString(" -o outgoing calls def=on\n");
InOut.WriteString(" -q suppress non-fatal errors def=off\n");
InOut.WriteString(" -nname calls to particular host def=off\n\n\n\n");
Arts.Exit(5);
END;
END;
END;
InOut.WriteLn; InOut.WriteLn;
IF DosL.GetVar(SYSTEM.ADR("XSTATARGS"),SYSTEM.ADR(temp),
strlen,DosD.VarFlagSet{})#-1 THEN
SplitArgs(temp);
END;
IF NOT Arts.wbStarted THEN
IF Arts.dosCmdLen>0 THEN
temp:=SYSTEM.CAST(StringTPtr,Arts.dosCmdBuf)^;
IF temp[String.Length(temp)-1]=ASCII.lf THEN
temp[String.Length(temp)-1]:=ASCII.nul;
END;
SplitArgs(temp);
END;
END;
IF DosL.CompareDates(SYSTEM.ADR(args.from),SYSTEM.ADR(args.to))<0 THEN
MyError("","illegal combination of parameters",
"FROM date is later than TO date",TRUE);
END;
IF NOT (args.in OR args.out) THEN
MyError("","illegal combination of parameters",
"you can't specify -i and -o simultaneously",TRUE);
END;
totali.connects:=0;
totali.inbb:=0;
totali.outbb:=0;
totali.inbn:=0;
totali.outbn:=0;
totali.cost:=0.0;
totali.units:=0;
totali.seconds:=0;
totali.bpeak:=0;
totali.npeak:=0;
totalo:=totali;
inf.fh:=DosL.Open(SYSTEM.ADR(args.xstatdata),DosD.readOnly);
IF inf.fh=NIL THEN
MyError("Can't open XStat.data!","filename was",args.xstatdata,TRUE);
END;
GetXStatData();
DosL.Close(inf.fh);
IF currency[0]=ASCII.nul THEN
MyError("WARNING","no currency definition found in XStat.data",
"using 'DM' as default",FALSE);
currency:=" DM";
END;
inf.fh:=DosL.Open(SYSTEM.ADR(args.xferstat),DosD.readOnly);
IF inf.fh=NIL THEN
MyError("Can't open Xferstat!","filename was",args.xferstat,TRUE);
END;
REPEAT
Break.TestBreak();
GetLog(ln1,ln2);
IF String.Length(ln1)+String.Length(ln2)>0 THEN
IF ParseLog(ln1,ln2,log) THEN
IF (args.host[0]=ASCII.nul) OR (String.Compare(args.host,log.host)=0) THEN
IF (DosL.CompareDates(SYSTEM.ADR(args.from),SYSTEM.ADR(log.sdate))>=0) AND
(DosL.CompareDates(SYSTEM.ADR(log.sdate),SYSTEM.ADR(args.to))>=0) THEN
IF ComputeCost(log) THEN
IF args.verbose THEN
DisplayStats(log);
END;
IF log.callout THEN
UpdateTotals(totalo,log);
ELSE
UpdateTotals(totali,log);
END;
ELSE
IF NOT args.quiet THEN
InOut.WriteString("(ignoring this one)\n\n\n\n");
END;
END;
END;
END;
ELSE
IF NOT args.quiet THEN
InOut.WriteString("(ignoring this one)\n\n\n\n");
END;
END;
END;
UNTIL inf.eof;
IF args.out THEN
DisplayTotals(totalo,TRUE);
END;
IF args.in THEN
DisplayTotals(totali,FALSE);
END;
CLOSE
IF xsroot#NIL THEN
FreeXData(xsroot);
xsroot:=NIL;
END;
IF inf.fh#NIL THEN
DosL.Close(inf.fh);
inf.fh:=NIL;
END;
END XStat.